/*
 * 教材中的示例程序之完整版.
 * 书名：《编译原理基础》
 * 版次：第三版
 * 作者：王献青，张立勇，张淑平，刘坚
 * 出版社：西安电子科技大学出版社
 * 程序完成时间：2025.03
 */

//
// 文件内容：为文法 G3.9" 编制的递归下降的预测分析器
//   根据教材中的程序清单 3.1 扩充得到
//
// 虽然下面程序中设立了错误恢复机制，但因过于简单，使得
// 错误恢复的处理效果并不好。
//

#include <stdbool.h>
#include <stdio.h>

#include "lexer.h"

TokenKind  lookahead;

static void L( );
static void E( );
static void T( );
static void F( );

static void fetchToken();

// 语法分析器的入口函数
void  parser_main( ) {
    fetchToken();   // 读取第一个记号
    fprintf(stdout, "Performs top-down parsing ...\n");

    // 调用开始符号的子程序，即开始推导
    L( );
    // 当调用返回后，语法分析结束

    fprintf(stdout, "Finished top-down parsing.\n");
}


static void error(const char *);
static bool match(TokenKind);


static void trace_enter(const char * non_terminal);
static void trace_exit(const char * non_terminal);
static void trace_match(TokenKind expected);


static void L( )  {                        	   // 展开非终结符L
    trace_enter(__FUNCTION__);

    // 条件选择依据(不完善)：  FOLLOW(L) = { EOF }
    while (lookahead != EOFS) {  // 遇到输入结束时分析结束
        E( );          // 调用E的子程序，即推导E，其他类似
        match(SEMICO);
    }

    trace_exit(__FUNCTION__);
}

static void E( )   {   		                   // 展开非终结符E
    trace_enter(__FUNCTION__);

    T( );

    //对于EBNF形式的子结构  ((+|–) T)* , 其 FIRST={ +, -, ε }
    while (lookahead==PLUS || lookahead==MINUS ) {
        match(lookahead);
        T( );
    }

    trace_exit(__FUNCTION__);
}

static void  T( ) {        			           // 展开非终结符T
    trace_enter(__FUNCTION__);

    F( );

    //对于EBNF形式的子结构  ((*|/|mod) F)* , 其 FIRST={ *, /, mod, ε }
    while(lookahead==MUL || lookahead==DIV || lookahead==MOD) {
        match(lookahead);
        F( );
    }

    trace_exit(__FUNCTION__);
}

static void F( ) {        			   	       // 展开非终结符F
    trace_enter(__FUNCTION__);

    //  F → '(' E ')' | id | num
    // 对于F的三个候选项，各自的 FIRST 集合中均不包括 ε，所以
    // 下面仅根据 FIRST 集合划分分支条件
    switch(lookahead) {
        case LP:    // FIRST( F→(E) ) = { ( }
            match(LP);       	      // '('
            E( );
            match(RP);              // ')'
            break;
        case ID :   // FIRST( F→id ) = { id }
            match(ID);
            break;
        case NUM : // FIRST( F→num ) = { num }
            match(NUM);
            break;
        default	:
            error("syntax error2");   // 出错处理
    }

    trace_exit(__FUNCTION__);
}


static void error(const char * msg) {
    printf("%s: bad input %s  [%s] at %d:%d\n",
           msg,
           lookup_kindname(lookahead),
           globToken.word, globToken.line, globToken.column);
}


//判断下一记号(来自输入)是否与期望的终结符(来自文法)匹配
// 若匹配则读取下一个记号并将记号类别存入lookahead
static bool match(TokenKind expected) {
    if (expected == lookahead) {
        trace_match( expected );  // 打印跟踪信息
        fetchToken();   // 获取下一记号
        return true;
    }
    error("syntax error1");               // 出错处理

    // 错误恢复： 简单地丢弃若干记号，等待期望的记号 或者 输入结束
    while( EOFS != lookahead ) {
        fetchToken();
        if ( lookahead == expected ) {
            // 等到了期待的记号---假装匹配上了，因此接着获取下一记号
            match(expected);
            break;
        }
    }

    return false;
}


//---------------------------------------------------------------
// 下面程序段用于显示 ： 递归下降函数的调用关系，及层次，
//  即：  语法分析的过程，
//  即：  最左推导过程

static int trace_deepth = 0;
static const char * trace_indent_blank =
        "                                                  "  // 50
        "                                                  "  // 100
        ;

//打印跟踪信息： 开始推导(展开)一个非终结符
static void trace_enter(const char * non_terminal) {
    fprintf(stdout, "%.*s%s >>\n",
            trace_deepth, trace_indent_blank,
            non_terminal);
    trace_deepth += 2;
}

//打印跟踪信息：结束对一个非终结符的推导(展开)
static void trace_exit(const char * non_terminal) {
    trace_deepth -= 2;
    fprintf(stdout, "%.*s%s <<\n",
            trace_deepth, trace_indent_blank,
            non_terminal);
}

//打印跟踪信息： 输入记号(终结符)和期望的终结符匹配上了
static void trace_match(TokenKind expected) {
    fprintf(stdout, "%.*s%s  **MATCHED**, [%s] at %d:%d\n",
            trace_deepth, trace_indent_blank,
            lookup_kindname(expected),
            globToken.word, globToken.line, globToken.column);
}

// 获取下一输入记号，并打印跟踪信息
static void fetchToken() {
    lookahead = nextToken();
    fprintf(stdout, "%.*s    fetched token: %s [%s] at %d:%d \n",
            trace_deepth, trace_indent_blank,
            lookup_kindname(globToken.kind),
            globToken.word, globToken.line, globToken.column);
}
